package com.maccman.activeresource
{
	import com.adobe.webapis.URLLoaderBase;
	
	import com.maccman.activeresource.IndexedCollection;
	
	import flash.net.URLRequest;
	import flash.events.Event;
	import mx.utils.StringUtil;
	import com.adobe.utils.DateUtil;
	import com.adobe.net.DynamicURLLoader;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.net.URLRequestHeader;
	import flash.net.URLRequestMethod;
	import flash.net.URLVariables;
	
	import mx.rpc.IResponder;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.AsyncToken;
	
	import mx.utils.ArrayUtil;
	import mx.controls.Alert;
	import flash.events.HTTPStatusEvent;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import com.adobe.net.URI;
	
	import com.maccman.net.SocketURLLoader;
	import com.maccman.aireo.model.AireoModelLocator;
	
	import flash.system.Security;
	
	dynamic public class ActiveResource extends URLLoaderBase
	{
		protected var _baseUrl:URI;
		protected var _resourceName:String;
		protected var _modelName:String;
		protected var _path:String;
		protected var _responder:IResponder;;
		protected var _username:String;
		protected var _password:String;
		protected var _current_id:Number;
		public static const GET:String = "GET"
		public static const PUT:String = "PUT"
		public static const POST:String = "POST"
		public static const DELETE:String = "DELETE"
		protected var _showBusyCursor:Boolean = false;
		
		public var remembrall:String;
		
		//private var loader:HTTPURLLoader;
		//private var loader:URLLoader;
		private var loader:SocketURLLoader;
		
		private static var model:AireoModelLocator = AireoModelLocator.getInstance()
		
		public function ActiveResource(baseUrl:URI, modelName:String, responder:IResponder){
		 	this._baseUrl = baseUrl;
		 	this._modelName = modelName
		 	this._resourceName =  modelName + 's';
		 	this._responder = responder
		}
		
		public function set showBusyCursor(t:Boolean) {
			this._showBusyCursor = t;
		}
		
		// Should be static?
		public function find_all(conditions:* = null):void {
			getAction(null,conditions);
		}
		public function find(id:Number,conditions:* = null):void {
			getAction(id.toString(),conditions);
		}
		
		public function save(data:Object):void {
			data.id ? update(data) : create(data)
		}
		
		protected function update(data:Object):void {
			_current_id = data.id;
			putAction(data);
		}
		
		protected function create(data:Object):void {
			postAction(data);
		}
		
		public function destroy(id:Number):void {
			_current_id = id;
			deleteAction(id);
		}
		
		public function set username(user:String):void {
			this._username = user;
		}
		
		public function set password(pass:String):void {
			this._password = pass;
		}
		
		public function set resource(res:String):void {
			this._resourceName = res;
		}
		
		protected function getAction(id:String = null, conditions:* = null):void{
			if(id){
			  connection(getActionResultHandler, GET, null, '/' + id, conditions);
			} else {
			  connection(getActionResultHandler, GET, null, '/', conditions);
			}
		}
		
		protected function getActionResultHandler(event:Event):void {
			  _responder.result(XML(event.target.data))
		}
		
		protected function putAction(data:Object):void {
			connection(putActionResultHandler, PUT, data, '/' + data.id);
		}
		
		protected function putActionResultHandler(event:Event):void {
			 find(_current_id)
		}
		
		protected function postAction(data:Object):void {
			connection(postActionResultHandler, POST, data, null);
		}
		
		protected function postActionResultHandler(event:Event):void {
			var loc:String = SocketURLLoader(event.target).responseHeaders['Location'];
			var num:Number = Number(loc.split('/').pop());
			find(num);
		}
		
		protected function deleteAction(id:Number):void {
			connection(deleteActionResultHandler, DELETE, null, '/' + id.toString());
		}
		
		protected function deleteActionResultHandler(event:Event):void {
			var o:Object = new Object();
			o.id = _current_id;
			_responder.result(o);
		}
		
		protected function parse_conditions(con:*):String {
			var st:String = new String();
			if (con is Array) {
				var r:Array = new Array();
				for ( var i:Number=0 ;i < con.length; i ++){
					r.push('conditions[]=' + URI.escapeChars(con[i]));
				}
				st = r.join('&')
			} else {
				st = 'conditions=' + URI.escapeChars(con)
			}
			return st;
		}
		
		protected function parse_data_into_string(data:Object):URLVariables {
			var ar:Array = new Array();
			for(var i:String in data) {
				ar.push(this._modelName + '[' + i + ']=' + data[i]);
			}
			return new URLVariables(ar.join('&'));
		}
		
		protected function progressHandler(event:ProgressEvent):void {
			model.requestProgress = "Loaded " + Math.round((event.bytesLoaded / event.bytesTotal) * 100) + "%";
		}
		
		protected function httpFaultHandler(event:HTTPStatusEvent):void {
			switch(event.status) {
				case 200:
				 break;
				case 401:
				 Alert.show( "Your session has expired, please log in again", "Session Expired" );
			     break;
			    case 500:
			     Alert.show( "Oops, Aireo has had an error, please try again in a few minutes", "Error" );
			     break;
			    case 409:
			     Alert.show( "You're trying to overright newer data, please refresh", "Stale Object Error" );
			     break;
			    default:
			     _responder.fault(event);
			}
		}
		
		protected function IOFaultHandler(event:Event):void {
			Alert.show( "There has been an error connecting to Aireo's servers, please make sure you're connected and try again.","Connection Error")
		}
		
		protected function securityFaultHandler(event:Event):void {
			Alert.show( "There has been a security error, please contact Aireo Support.", "Security Error" )
		}
		
		protected function connection(resultHandler:Function, method:String, data:Object = null, extraUrlInfo:String = null, conditions:* = null):void {
			//loader = new HTTPURLLoader();
			//loader = new URLLoader;
			loader = new SocketURLLoader();
			loader.addEventListener(Event.COMPLETE, resultHandler);
			loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, httpFaultHandler)
			loader.addEventListener(IOErrorEvent.IO_ERROR, IOFaultHandler)
			loader.addEventListener(ProgressEvent.PROGRESS, progressHandler)
			loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityFaultHandler)
			//need to listen for errors
			
			var request:URLRequest = new URLRequest();
			request.url = this._baseUrl.toString() + '/' + this._resourceName;
			if (extraUrlInfo) {
				request.url += extraUrlInfo;
			}
			if (data) {
				request.data = parse_data_into_string(data);
			} //else {
			//	request.data = new Object();
			//}
			//request.method = URLRequestMethod.POST;
			loader.method = method;
			
			if(_username) {
				var credentials:String = "";
				var authHeader:URLRequestHeader = new URLRequestHeader("Authorization","Basic " + credentials);
				request.requestHeaders.push(authHeader);
			}
			
			//request.url += "?_method=" + method
		
			if (conditions) {
				request.url += "&" + parse_conditions(conditions)
			}
			
			if (remembrall) {
				request.requestHeaders.push(new URLRequestHeader("Cookie","remembrall=" + remembrall));
			}
			
			//loader.httpUrl = new URI(this._baseUrl.toString() + '/' + this._resourceName)
			//loader.httpMethod = method;
			
			Security.loadPolicyFile(_baseUrl.toString() + '/crossdomain.xml');
			
			loader.showBusyCursor = _showBusyCursor;
			loader.requestTimeout = 6;
			
			loader.load(request);
		}
		
	}
}